#pragma once
#include<iostream>
#include<string>
#include<memory>
#include"Socket.hpp"

using namespace socket_ns;
class selectServer
{
    const static int N = sizeof(fd_set)*8;
    const static int defaultfd = -1;
    public:
    selectServer(uint16_t port)
    :_port(port)
    ,_listensock(std::make_unique<TcpSocket>())
    {
        InetAddr addr("0",_port);
        _listensock->BuildListenSocket(addr);
        for(int i = 0;i<N;i++)
        {
            _fd_array[0] =defaultfd;
        }
        _fd_array[0] = _listensock->SockFd();
    }
    void AcceptClient()
    {
        //只需要读 读函数listensock nornam sockfd
        InetAddr clientaddr;
        int sockfd = _listensock->Accepter(&clientaddr);//不会阻塞 因为事件就绪了
        if(sockfd<0)
        {
            return ;
        }
        LOG(DEBUG,"get new Link,sockfd:%d client info %s\n",sockfd,clientaddr.Ip().c_str(),clientaddr.Port());
        int pos =1;
        for(;pos<N;pos++)
        {
            if( _fd_array[pos]==defaultfd)
            {
                break;;
            }
            if(pos==N)
            {
                ::close(sockfd);
                LOG(WARNING,"server is full!\n");
                return;
            }
            else
            {
                _fd_array[pos] = sockfd;
                LOG(DEBUG,"%d add to select addrt!\n",sockfd);
            }
            LOG(DEBUG,"curr fd_array[] fd list :%s\n",RfdsToString().c_str());
        }
    }
    void ServiceIO(int pos)
    {
        char buffer[1024];
        ssize_t n = ::recv(_fd_array[pos],buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            buffer[n] = 0;
            std::cout<<"client say# "<<buffer<<std::endl;
            std::string echo_string = "[server echo]# ";
            echo_string +=buffer;
            ::send(_fd_array[pos],echo_string.c_str(),echo_string.size(),0);
        }
        else if(n==0)
        {
            LOG(DEBUG,"%d is closed\n",_fd_array[pos]);
            ::close(_fd_array[pos]);
            _fd_array[pos] = defaultfd;
            LOG(DEBUG,"curr fd_array[] fd list :%s\n",RfdsToString().c_str());
        }
        else
        {
            LOG(DEBUG,"%d recv error\n",_fd_array[pos]);
            ::close(_fd_array[pos]);
            _fd_array[pos] = defaultfd;
            LOG(DEBUG,"curr fd_array[] fd list :%s\n",RfdsToString().c_str());
        }
    }
    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < N; i++)
        {
            if (_fd_array[i] == defaultfd)
                continue;
            if (FD_ISSET(_fd_array[i], &rfds))
            {
                if (_fd_array[i] == _listensock->SockFd())
                {
                    AcceptClient();
                }
                else
                {
                    ServiceIO(i);
                }
            }
        }
    }
    void loop()
    {
        while (true)
        {
            //listensocket 等待新连接，等价于对方给我发送数据，作为读事件统一处理
            //新连接到来 事件就绪
            //首先将listensock天骄到select中
            fd_set rfds;
            FD_ZERO(&rfds);//清除；
            int max_fd = defaultfd;
            for(int i = 0;i<N;i++)
            {
                if(_fd_array[i] == defaultfd)
                 continue;;
                 FD_SET(_fd_array[i],&rfds);//将所有合法的fd添加到rfds中
                 if(max_fd<_fd_array[i])
                 {
                    max_fd = _fd_array[i];//更新最大的fd值
                 }
            } 
            struct timeval timeout = {0,0};
            int  n = select(max_fd+1,&rfds,nullptr,nullptr,nullptr);
            switch (n)
            {
            case 0:
                LOG(INFO,"timeout %d.%d\n",timeout.tv_sec,timeout.tv_usec);
                break;
            case -1:
                LOG(ERROR,"select error...\n");
                break;
            default:
                LOG(DEBUG,"evebt Happen. n:%d\n",n);//只要有一个事件就绪 select就会一直通知，
                //等待处理
                HandlerEvent(rfds);
                break;
            }
        }
        
    }
    std::string RfdsToString()
    {
        std::string fdstr;
        for(int i = 0;i<N;i++)
        {
            if(_fd_array[i]==defaultfd)
             continue;
             fdstr +=std::to_string(_fd_array[i]);
             fdstr+=" ";
        } 
        return fdstr;
    }
    ~selectServer()
    {}
    private:
    uint16_t _port;
    std::unique_ptr<Socket>_listensock;
    int _fd_array[N];
};
